﻿using System;
using System.Collections.Generic;
using System.Text;
using sb0t_winGUI;

namespace cb0tServer
{
    class Connect4
    {
        public Connect4User player1;
        public Connect4User player2;
        public ushort vroom;

        private byte turn;

        private byte[,] cells = new byte[6, 7] // score board [row, column]
        {
            { 0, 0, 0, 0, 0, 0, 0 },
            { 0, 0, 0, 0, 0, 0, 0 },
            { 0, 0, 0, 0, 0, 0, 0 },
            { 0, 0, 0, 0, 0, 0, 0 },
            { 0, 0, 0, 0, 0, 0, 0 },
            { 0, 0, 0, 0, 0, 0, 0 }
        };

        public Connect4(UserObject player1, UserObject player2)
        {
            this.player1 = new Connect4User(player1);
            player1.connect4 = true;
            this.player2 = new Connect4User(player2);
            player2.connect4 = true;
            this.SetVroom();
            this.turn = (byte)Helpers.rnd.Next(1, 3);

            if (turn > 2)
                turn = 2;
        }

        public void Start()
        {
            UserPool.Broadcast(this.vroom, ServerOutboundPackets.AnnoucePacket("\x000314--- Welcome to Connect 4 Game - to quit type #c4 stop"));

            this.ResetScoreBoard();
            this.DrawGame();
        }

        public void OnText(UserObject userobj, String text)
        {
            if (this.turn == 1)
            {
                if (userobj.id != this.player1.id)
                    return;
            }
            else if (this.turn == 2)
                if (userobj.id != this.player2.id)
                    return;

            int[] requested_coordinate = this.TextToPosition(text); // [row, column]

            if (requested_coordinate == null)
                return;

            if (requested_coordinate[0] == -1)
            {
                UserPool.Broadcast(this.vroom, ServerOutboundPackets.AnnoucePacket("\x000314--- Column " + (text.Trim().ToUpper()) + " is full, try another."));
                return;
            }

            this.cells[requested_coordinate[0], requested_coordinate[1]] = this.turn;
            this.turn = (this.turn == 1 ? (byte)2 : (byte)1);

            this.DrawGame();

            if (this.IsWinner(requested_coordinate[0], requested_coordinate[1]))
            {
                for (int i = 0; i < 6; i++)
                    UserPool.Broadcast(this.vroom, ServerOutboundPackets.AnnoucePacket("\x000314--- (H) (H) (H) " + userobj.name + " is the winner!! (H) (H) (H)"));

                UserPool.Broadcast(this.vroom, ServerOutboundPackets.AnnoucePacket(""));
                this.Start();
                return;
            }
            else if (this.IsDrawn)
            {
                UserPool.Broadcast(this.vroom, ServerOutboundPackets.AnnoucePacket("\x000314--- It's a draw!!!"));
                this.Start();
                return;
            }
        }

        private bool IsWinner(int x, int y) // rule logic
        {
            String required = "";

            for (int i = 0; i < 4; i++)
                required += (this.turn == 1 ? 2 : 1).ToString();

            String checker = "";

            // vertical check
            for (int i = 0; i < 6; i++)
                checker += cells[i, y].ToString();

            if (checker.Contains(required))
                return true;

            checker = "";

            // horizontal check
            for (int i = 0; i < 7; i++)
                checker += cells[x, i].ToString();

            if (checker.Contains(required))
                return true;

            // diag checks - it's actually less code checking this way
            List<String> diags = new List<String>();
            diags.Add(cells[3, 0] + "" + cells[2, 1] + "" + cells[1, 2] + "" + cells[0, 3]);
            diags.Add(cells[4, 0] + "" + cells[3, 1] + "" + cells[2, 2] + "" + cells[1, 3] + "" + cells[0, 4]);
            diags.Add(cells[5, 0] + "" + cells[4, 1] + "" + cells[3, 2] + "" + cells[2, 3] + "" + cells[1, 4] + "" + cells[0, 5]);
            diags.Add(cells[5, 1] + "" + cells[4, 2] + "" + cells[3, 3] + "" + cells[2, 4] + "" + cells[1, 5] + "" + cells[0, 6]);
            diags.Add(cells[5, 2] + "" + cells[4, 3] + "" + cells[3, 4] + "" + cells[2, 5] + "" + cells[1, 6]);
            diags.Add(cells[5, 3] + "" + cells[4, 4] + "" + cells[3, 5] + "" + cells[2, 6]);
            diags.Add(cells[0, 3] + "" + cells[1, 4] + "" + cells[2, 5] + "" + cells[3, 6]);
            diags.Add(cells[0, 2] + "" + cells[1, 3] + "" + cells[2, 4] + "" + cells[3, 5] + "" + cells[4, 6]);
            diags.Add(cells[0, 1] + "" + cells[1, 2] + "" + cells[2, 3] + "" + cells[3, 4] + "" + cells[4, 5] + "" + cells[5, 6]);
            diags.Add(cells[0, 0] + "" + cells[1, 1] + "" + cells[2, 2] + "" + cells[3, 3] + "" + cells[4, 4] + "" + cells[5, 5]);
            diags.Add(cells[1, 0] + "" + cells[2, 1] + "" + cells[3, 2] + "" + cells[4, 3] + "" + cells[5, 4]);
            diags.Add(cells[2, 0] + "" + cells[3, 1] + "" + cells[4, 2] + "" + cells[5, 3]);

            foreach (String str in diags)
                if (str.Contains(required))
                    return true;

            return false;
        }

        private bool IsDrawn
        {
            get
            {
                int full_columns = 0;

                for (int x = 0; x < 7; x++)
                    if (this.cells[0, x] > 0)
                        full_columns++;

                return full_columns == 7;
            }
        }

        private void ResetScoreBoard()
        {
            for (int x = 0; x < 6; x++)
                for (int y = 0; y < 7; y++)
                    cells[x, y] = 0;
        }

        private void DrawGame()
        {
            List<String> lines = new List<String>();
            lines.Add("");
            lines.Add("   \x000501\x000309#  A    B    C    D    E     F    G #"); // top line
            lines.Add("   \x000501\x000309######################");

            for (int x = 0; x < 6; x++)
            {
                String str = "   \x000501\x000309#";

                for (int y = 0; y < 7; y++)
                {
                    switch (this.cells[x, y])
                    {
                        case 0: // free
                            str += "    \x000501#";
                            break;

                        case 1: // player 1 (red)
                            str += "\x000504    \x000501#";
                            break;

                        case 2: // player 2 (yellow)
                            str += "\x000508    \x000501#";
                            break;
                    }
                }

                if (x == 2)
                    str += "\x000500   \x000501\x000304 " + player1.name + (turn == 1 ? " [your turn] " : " ");
                else if (x == 3)
                    str += "\x000500   \x000501\x000308 " + player2.name + (turn == 2 ? " [your turn] " : " ");

                lines.Add(str);
                lines.Add("   \x000501\x000309######################");
            }

            UserObject p1 = UserPool.GetUserById(this.player1.id);
            UserObject p2 = UserPool.GetUserById(this.player2.id);

            lines.Add("");

            if (p1 != null && p2 != null)
                foreach (String str in lines)
                    UserPool.Broadcast(this.vroom, ServerOutboundPackets.AnnoucePacket(str));
        }

        private int[] TextToPosition(String text)
        {
            int x = -1;

            switch (text.ToUpper().Trim())
            {
                case "A":
                    x = 0;
                    break;

                case "B":
                    x = 1;
                    break;

                case "C":
                    x = 2;
                    break;

                case "D":
                    x = 3;
                    break;

                case "E":
                    x = 4;
                    break;

                case "F":
                    x = 5;
                    break;

                case "G":
                    x = 6;
                    break;
            }

            if (x == -1)
                return null; // invalid input

            for (int i = 5; i > -1; i--)
                if (this.cells[i, x] == 0)
                    return new int[] { i, x }; // first free cell in column

            return new int[] { -1, x }; // column is full
        }

        private void SetVroom()
        {
            this.vroom = 1;

            while (true)
            {
                bool vroom_ok = true;

                foreach (UserObject u in UserPool.Users)
                {
                    if (u.IsSet())
                    {
                        if (u.logged_in)
                        {
                            if (u.vroom == this.vroom)
                            {
                                this.vroom++;
                                vroom_ok = false;
                                break;
                            }
                        }
                    }
                }

                if (vroom_ok)
                    break;
            }
        }

    }

    class Connect4User
    {
        public ushort id;
        public String name;

        public Connect4User(UserObject userobj)
        {
            this.id = userobj.id;
            this.name = userobj.name;
        }
    }
}
